<template>
  <div
    class="ag-grid__wrapper"
    :style="gridStyle"
  >
    <!-- 查询条件 -->
    <div class="search">
      <slot />
    </div>
    <!-- 表格 -->
    <div class="ag-grid">
      <ag-grid-vue
        ref="agGrid"
        :modules="modules"
        style="{width:100%;height:100%;}"
        :class="className"
        :row-data="rowData"
        :edit-type="editType"
        :pagination="pagination"
        :column-defs="newColDefs"
        :grid-options="gridOptions"
        :pagination-page-size="paginationPageSize"
        :framework-components="frameworkComponents"
        :row-selection="rowSelection"
        @gridReady="onGridReady"
        @modelUpdated="onModelUpdated"
        @gridSizeChanged="onGridSizeChanged"
        @rowClicked="handleRowClicked"
        @rowSelected="handleRowSelected"
        @cellClicked="handleCellClicked"
        @cellValueChanged="handleCellValueChanged"
      />
    </div>
  </div>
</template>

<script>
import Schema from 'async-validator'
import select from '../component/select'
import datePicker from '../component/datePicker'
import { AgGridVue } from '@ag-grid-community/vue'
import { AllModules } from '@ag-grid-enterprise/all-modules'

export default {
  name: 'AgGrid',
  components: { AgGridVue },
  props: {
    /**
     * 列定义，参照 Ag-Grid Column Definitions
     * cellEditorParams --> 编辑模式组件的自定义属性放在这里
     * cellEditorParams.componentProp --> 编辑模式 element ui 组件属性放在这里
     */
    columnDefs: { type: Array },

    /** 常用属性 */
    editType: { type: String },
    rowData: { type: Array, required: true },
    rowSelection: { type: String, default: 'single' },
    className: { type: String, default: 'ag-theme-balham' },
    pagination: { type: Boolean, default: false },
    paginationPageSize: { type: Number },

    /** 注册框架组件 */
    frameworkComponents: { type: Object, default: () => { return {} } },

    /** 所有表格属性或事件都可以在这里定义 */
    gridOptions: { type: Object, default: () => { return {} } }
  },
  data () {
    return {
      gridStyle: null,
      newColDefs: [],
      modules: AllModules
    }
  },
  mounted () {
    this.gridStyle = this.$el.style.cssText
  },
  beforeMount () {
    // 在子组件中代理父组件
    this.gridOptions.context = { componentParent: this }
    this.gridOptions.singleClickEdit = true
    // 自定义 column type
    this.gridOptions.columnTypes = {
      Select: { cellEditor: 'Select' },
      DatePicker: { cellEditor: 'DatePicker' },
      NumberInput: { valueParser: params => Number(params.newValue) }
    }
    this.gridOptions.enableBrowserTooltips = true
    // 设置表格默认属性
    this.gridOptions.defaultColDef = Object.assign(this.gridOptions.defaultColDef || {}, {
      // resizable: true,
      tooltipValueGetter: params => {
        return params.colDef.valueFormatter ? params.colDef.valueFormatter(params) : params.value
      }
    })
    this.frameworkComponents.DatePicker = datePicker
    this.frameworkComponents.Select = select
    // columnDefs 变更引起列表重绘，导致 Select 查询时列表不断刷新，暂时赋值给 newColDefs，columnDefs 动态取值用
    this.newColDefs = this.columnDefs
  },
  methods: {
    /**
     * 手动重绘列表
     * columnDefs 如有更改，需手动重绘
     * @param {Array} columnDefs
     */
    redrawGrid (columnDefs) {
      this.newColDef = columnDefs
      this.gridOptions.api.setColumnDefs(this.newColDef)
      // TODO: 监听列宽、排序等行为，避免重绘时一些属性恢复默认值
      // https://www.ag-grid.com/javascript-grid-column-definitions/
    },
    onGridReady (params) {
      this.$emit('gridReady', params)
    },
    onModelUpdated (params) {
      params.api.sizeColumnsToFit() // 列宽自适应
      this.$emit('modelUpdated', params)
    },
    onGridSizeChanged (params) {
      params.api.sizeColumnsToFit() // 列宽自适应
      this.$emit('gridSizeChanged', params)
    },
    handleCellClicked (params) {
      this.$emit('cellClicked', params)
    },
    handleCellValueChanged (params) {
      this.$emit('cellValueChanged', params)
    },
    handleRowClicked (params) {
      this.$emit('rowClicked', params)
    },
    handleRowSelected (params) {
      this.$emit('rowSelected', params)
    },
    /**
     * 表格校验
     * @param {Object} rowNode 目标行
     * @param {Function} callback 校验后的回调
     */
    validate (rowNode, callback) {
      this.gridOptions.api.stopEditing()

      let flag = true
      const rowData = rowNode.data
      const rowIndex = rowNode.rowIndex
      const displayedColumns = this.gridOptions.api.columnController.allDisplayedColumns

      /** 逐格校验 */
      const validateField = (columnDefs) => {
        let field
        let colDef
        let cellEditorParams

        for (const i in columnDefs) {
          if (!flag) return

          colDef = columnDefs[i].colDef
          field = colDef.field
          cellEditorParams = colDef.cellEditorParams

          if (colDef.editable && cellEditorParams) {
            if (cellEditorParams.rules) {
              const descriptor = { [field]: typeof cellEditorParams.rules === 'function' ? cellEditorParams.rules({ data: rowData }) : cellEditorParams.rules }
              const validator = new Schema(descriptor)
              // https://www.npmjs.com/package/async-validator/v/1.8.5
              // 使用lodash的this.$_.get(rowData, field)获取数据 支持传路径，根据 object对象的path路径获取值。 Change By LFLJ
              validator.validate({ [field]: this.$_.get(rowData, field) }, (errors, fields) => {
                if (errors) {
                  flag = false
                  this.focusOnCell(rowIndex, columnDefs[i].colId)
                  if (colDef.customMessage) {
                    colDef.customMessage()
                  } else {
                    let errorMessage = errors[0].message
                    if (errorMessage.indexOf('is not a number') > -1) {
                      this.$alert(`Column ${colDef.headerName} must all be numbers.`, 'Warning')
                    } else if (errorMessage.indexOf('is required') > -1) {
                      this.$alert(`The grid column ${colDef.headerName} is required.`, 'Warning')
                    }
                  }
                }
              })
            }
          }
        }
      }

      validateField(displayedColumns)
      callback(flag)
    },
    validateAll () {
      return new Promise((resolve, reject) => {
        this.$utils.stopEditing(this)
          .then(() => {
            let result = true
            if (this.rowData.length) {
              this.rowData.forEach((row) => {
                if (result) {
                  this.validate({ data: row }, (flag) => {
                    result = flag
                  })
                }
              })
            }
            if (result) {
              resolve(true)
            } else {
              reject(new Error())
            }
          })
      })
    },
    /** 查找 coldef */
    findColDef (colDef, columnDefs) {
      let key = 'field'
      for (const i in columnDefs) {
        if (!colDef.colId && !colDef.field) {
          throw new Error('Property field or colId is required in column definitions')
        }
        if (colDef.colId) key = 'colId'
        if (columnDefs[i][key] === colDef[key]) {
          return columnDefs[i]
        } else if (columnDefs[i].children) {
          return this.findColDef(colDef, columnDefs[i].children)
        }
      }
    },
    focusOnCell (rowIndex, colId) {
      this.gridOptions.api.setFocusedCell(rowIndex, colId)
      this.gridOptions.api.startEditingCell({ rowIndex: rowIndex, colKey: colId })
    },

    // public methods
    // call official api to redraw the rows
    redrawRows (rowNodes) {
      this.gridOptions.api.redrawRows({ rowNodes })
    }
  }
}
</script>

<style lang="less" scoped>
  .ag-grid__wrapper {
    display: flex;
    flex-direction: column;
    .search {
      /deep/ .el-form-item {
        margin-bottom: 10px;
      }
    }
    .ag-grid {
      flex: 1;
    }
  }
  /deep/.cell-span{
    background: white;
    border-left: 1px solid lightgrey !important;
    border-right: 1px solid lightgrey !important;
    border-bottom: 1px solid lightgrey !important;
    display: flex;
    align-items: center;
    justify-content: center;
  }
</style>
